#include<sys/types.h>
#include<sys/socket.h>
#include<stdio.h>
#include<netinet/in.h>
#include<unistd.h>
#include<pthread.h>
#include<mysql.h>
#include<string.h>
#include"online.h"
#include"msgqueue.h"
#include<time.h>
#include<stdlib.h>
#include<arpa/inet.h>

void parse(struct Package *pkg);//解析函数声明
//以下为全局都将使用的变量
MYSQL *conn_ptr=NULL;

int server_sockfd;
struct sockaddr_in server_address;
struct Online on;
struct Msgqueue queue;

void * handle_request(void *argv)//被用作线程的函数
{
    struct Package buff;
    int i = *((int *)argv);
    printf("thread Id %d is working\n",i);
    for(;;){
        read(i,&buff,sizeof(buff));
        buff.sock = i;//标识由那个sock客户端发来的信息,!!小心犯错，指针的赋值，因为，你的指针很可能变了，指的不是你原来的那个了
        printf("sock %d 添加一个包\n",buff.sock);
        add_pkg(&queue,&buff);
        if(buff.type == 3){
            printf("注销sock%d\n",buff.sock);
            close(i);
            pthread_exit(NULL);
        }
    }
}
//服务器传递服务器sock，此时每产生一个连接服务就生成一个线程
void handle_connect()//服务器连接，分配给每个客户端一个线程，用于接收客户端发送的消息
{
    int s_c;//接受产生的client，socket
    struct sockaddr_in client_address;
    int len = sizeof(client_address);
    pthread_t thread_do[10];
    int err[10];
    int i=0;
    printf("等待客户连接\n");
    for(;;){
        s_c = accept(server_sockfd,(struct sockaddr *)&client_address,&len);//返回连接的client的socket,可以理解为，已经监听，连接一个接受一个，此为一致接受
        if(s_c > 0){
            add(&on,s_c,-1);
            printf("客户端%d已连接",s_c);
            err[i] = pthread_create(&(thread_do[i]),NULL,handle_request,(void *)&s_c);//创建线程
            i++;
        }
        else
            printf("problem\n");
    }
}
MYSQL* connect_mysql()//数据库连接
{
    MYSQL * conn_ptr=NULL;
    conn_ptr = mysql_init(NULL);
    if( !conn_ptr){
        fprintf(stderr,"mysql error\n");
        return 0;
    }
    conn_ptr = mysql_real_connect(conn_ptr,"localhost","root","slmysql","TXDB",0,NULL,0);
    if(conn_ptr)
        printf("connect sucess\n");
    else
        printf("connect failed\n");
    return conn_ptr;
}

void parse(struct Package *pkg)//线程，包解析，只有一个包解析线程
{
    struct Package buff;
    char execsql[512];
    char s[50];
    switch(pkg->type){
    case 1:{
        {
            buff.destination = time((time_t *)0);//一值多用哦
            sprintf(s,"%d",buff.destination);
        }
        sprintf(execsql,"insert into User value('%s','%s','%s')",s,
                s,s);//插入用户名，密码
        if(!mysql_query(conn_ptr,execsql)){
            printf("新用户已登记\n");
            buff.type = 1;
        }
        else{
            printf("用户名密码登记失败\n");
            buff.type= -1;
        }
        sleep(1);
        printf("生成帐号%d\n",buff.destination);
        write((*pkg).sock,&buff,sizeof(buff));
        break;
    }
    case 2:{
        MYSQL_RES * res;
        printf("id :%d,password:%s验证\n",(*pkg).destination,(*pkg).msg);
        sprintf(execsql,"select Id from User where Id='%d' and Password='%s'",(*pkg).destination,(*pkg).msg);
        if(!mysql_query(conn_ptr,execsql)){
            res = mysql_store_result(conn_ptr);
            if(mysql_num_rows(res) == 1){//返回登录成功信息，并重新添加socket值绑定
                buff.type = 2;//标识验证成功
                buff.destination = pkg->destination;//登录者的帐号
                write((*pkg).sock,&buff,sizeof(buff));
                printf("添加完整在线信息sock:%d  id:%d\n",(*pkg).sock,(*pkg).destination);
                add(&on,(*pkg).sock,(*pkg).destination);
                mysql_free_result(res);
            }
            else{
                buff.type = -2;
                write((*pkg).sock,&buff,sizeof(buff));
            }

        }
        else{
            buff.type = -2;
            write((*pkg).sock,&buff,sizeof(buff));
        }
        break;
        //更改在线文件，返回登录信息
    }
    case 3:{
        printf("清楚%d在线文件\n",pkg->sock);//在线程退出后的buff给pkg，尚未证实指针值是否可用，虽然有如期结果
        dele_socket(&on,pkg->sock);
        break;
    }
    case 4:{//*pkg才是发来的东西，不是buff！！！！
        buff = *pkg;
        buff.type = 4;
        struct tm * current;
        time_t the_time;
        the_time = time(NULL);
        current = localtime(&the_time);
        sprintf(s,"%04d%02d%02d%02d%02d%02d",current->tm_year+1900,current->tm_mon+1, current->tm_mday,current->tm_hour,current->tm_min,current->tm_sec);
        sprintf(execsql,"insert into Chat values('%d','%d','%s','%s')",find_id(&on,pkg->sock),pkg->destination,s,pkg->msg);//插入聊天记录
        printf("%s\n",execsql);
        if(!mysql_query(conn_ptr,execsql)){
            printf("添加一条信息记录\n");
        }
        else{
            printf("添加信息失败\n");
        }
        buff.destination = find_id(&on,pkg->sock);
        write(find_socket(&on,(*pkg).destination),&buff,sizeof(buff));
        break;
    }
    case 5:{
        //查找数据库用户表，是否有该用户，写入未读事件表，更新至缓存队列中
        //当对方回应后，更新该用户好友列表
        buff.type = 5;
        buff.destination = find_id(&on,pkg->sock);
        if(find_socket(&on,pkg->destination) == -1){
            printf("无此在线用户");
            break;
        }
        write(find_socket(&on,pkg->destination),&buff,sizeof(buff));
        printf("已处理%d加%d为好友请求",buff.destination,pkg->destination);
        break;
    }
    case 6:{
        char sqldele[120];
        sprintf(execsql,"delete from Friend where Id='%d' and Id_Friend = '%d'",
                find_id(&on,pkg->sock),pkg->destination);
        sprintf(sqldele,"delete from Friend where Id='%d' and Id_Friend = '%d'",
                pkg->destination,find_id(&on,pkg->sock));
        if(!mysql_query(conn_ptr,execsql) && !mysql_query(conn_ptr,sqldele)){
            printf("删除好友成功\n");
            buff.type = 6;
            write(pkg->sock,&buff,sizeof(buff));
        }
        else{
            printf("删除失败\n");
            buff.type = -6;
            write(pkg->sock,&buff,sizeof(buff));
        }
        break;
    }
    case 7:{
        MYSQL_RES *res_ptr;
        MYSQL_ROW row;
        sprintf(execsql,"select Id_Friend,Name_Friend from Friend where Id='%d'",find_id(&on,pkg->sock));
        if(!mysql_query(conn_ptr,execsql)){
            res_ptr = mysql_store_result(conn_ptr);
            buff.type = 7;
            if(!mysql_num_rows(res_ptr)){
                buff.destination = 10000;
                sscanf("SERVER","%s",buff.msg);
                write(pkg->sock,&buff,sizeof(buff));
                break;
            }
            //sql查询会返回row的值，如果
            while((row = mysql_fetch_row(res_ptr))){
                sscanf(row[0],"%d",&buff.destination);
                sscanf(row[1],"%s",buff.msg);
                printf("在线好友：%d\t%s\n",buff.destination);

                write(pkg->sock,&buff,sizeof(buff));
            }
            mysql_free_result(res_ptr);
        }
        else{
            printf("查询好友失败\n");
        }
        break;
    }
    case 8:{
        char ss[120];
        MYSQL_RES *res_ptr;
        MYSQL_ROW row;
        sprintf(execsql,"insert into Friend value('%d','%d','null','null')",pkg->destination,find_id(&on,pkg->sock));
        sprintf(ss,"insert into Friend value('%d','%d','null','null')",find_id(&on,pkg->sock),pkg->destination);
        printf("同意好友请求%d,%d",pkg->destination,find_id(&on,pkg->sock));
        if(!mysql_query(conn_ptr,execsql) && !mysql_query(conn_ptr,ss)){
            printf("好友添加成功\n");
            //            buff.type = 8;
            //            write(find_socket(&on,pkg->destination),&buff,sizeof(buff));
            {
                sprintf(execsql,"select * from Friend where Id='%d' and Id_Friend = '%d'",
                        pkg->destination,find_id(&on,pkg->sock));
                if(!mysql_query(conn_ptr,execsql)){
                    res_ptr = mysql_store_result(conn_ptr);
                    buff.type = 7;
                    if(!mysql_num_rows(res_ptr))
                        break;
                    if(row = mysql_fetch_row(res_ptr)){
                        sscanf(row[0],"%d",&buff.destination);
                        sscanf(row[2],"%s",buff.msg);
                        printf("%d,%s",buff.destination,buff.msg);
                        write(pkg->sock,&buff,sizeof(buff));//写到接受者,buff.destination 为同意者的号码
                        sscanf(row[0],"%d",&buff.destination);
                        int sock = find_socket(&on,buff.destination);
                        sscanf(row[1],"%d",&buff.destination);
                        sscanf("null","%s",buff.msg);
                        write(sock,&buff,sizeof(buff));
                    }
                }
                mysql_free_result(res_ptr);
            }
        }
        else{
            printf("好友添加失败\n");
            buff.type = -8;
            write(find_socket(&on,pkg->destination),&buff,sizeof(buff));
        }
        break;
    }
    case 10:
    {
        printf("来自%d,文件%s传输指令(请求)\n",find_id(&on,pkg->sock),pkg->other);
        {
            //只需要知道，指令，对方id，文件名称即可
            buff.type = 10;
            buff.sock = find_id(&on,pkg->sock);//发送者id号
            sprintf(buff.other,"%s",pkg->other);
        }
        write(find_socket(&on,pkg->destination),&buff,sizeof(buff));
        break;
    }
    case -10:
    {
        printf("拒绝接受\n");
        {
        	buff.type = -10;
        	
        }
        break;
    }

    case 11:
    {
        printf("同意接收\n");
        struct sockaddr_in addr;
        memset(&addr,0,sizeof(addr));
        int len = sizeof(addr);
        int ret = getsockname(pkg->sock,(struct sockaddr*)&addr,(socklen_t*)&len);
        if(ret !=0){
            printf("getsockname() error \n");
            return;
        }
        char * s = inet_ntoa(addr.sin_addr);
        printf("IP:%s\n",s);
        {
            buff.type = 11;
            sprintf(buff.other,"%s",pkg->other);
            buff.destination = find_id(&on,pkg->sock);
            sprintf(buff.msg,"%s",s);
        }
        write(find_socket(&on,pkg->destination),&buff,sizeof(buff));
        break;
    }
    default:
        printf("未知请求类型%d\n",pkg->type);
    }
}

void *handle_parse()//删除包并解析
{
    struct Package buff;
    for(;;){
        if(!empty_msg(&queue)){
            ret_dele_pkg(&queue,&buff);
            parse(&buff);
        }
        sleep(1);
        printf("waiting handle client request\n");
    }
}

void init_server()//初始化网路套接字
{
    int server_len;
    pthread_t do_queue;
    server_sockfd = socket(AF_INET,SOCK_STREAM,0);//系统调用

    server_address.sin_family = AF_INET;
    server_address.sin_addr.s_addr = htonl(INADDR_ANY);
    server_address.sin_port = 9114;
    server_len = sizeof(server_address);
    //绑定套接字和具体地址
    bind(server_sockfd,(struct sockaddr *)&server_address,server_len);
    //进行监听，5个
    listen(server_sockfd,5);
    int err = pthread_create(&do_queue,NULL,handle_parse,NULL);
}


int main()
{
    printf("initMySQL\n");
    conn_ptr = connect_mysql();      //链接数据库
    printf("initOnline file\n");
    init_online(&on);//初始化在线文件
    printf("initOnline msgqueue\n");
    init_msg(&queue);//初始化服务器消息队列
    printf("initserver\n");
    init_server();//初始化服务器

    handle_connect();//等待接客户端
    printf("close server\n");
    close(server_sockfd);
    return 0;
}
